// Cz Cranelift 后端 - Zig 绑定
const std = @import("std");

// C FFI 声明
const CBackend = opaque {};

extern fn cranelift_backend_new() ?*CBackend;
extern fn cranelift_backend_free(backend: *CBackend) void;
extern fn cranelift_compile_simple_function(
    backend: *CBackend,
    name: [*]const u8,
    name_len: usize,
) bool;
extern fn cranelift_emit_object(
    backend: *CBackend,
    output: *?[*]u8,
    output_len: *usize,
) bool;
extern fn cranelift_free_buffer(ptr: [*]u8, len: usize) void;

pub const CraneliftBackend = struct {
    handle: *CBackend,
    allocator: std.mem.Allocator,

    pub fn init(allocator: std.mem.Allocator) !CraneliftBackend {
        const handle = cranelift_backend_new() orelse return error.InitFailed;
        return CraneliftBackend{
            .handle = handle,
            .allocator = allocator,
        };
    }

    pub fn deinit(self: *CraneliftBackend) void {
        cranelift_backend_free(self.handle);
    }

    pub fn compileSimpleFunction(self: *CraneliftBackend, name: []const u8) !void {
        if (!cranelift_compile_simple_function(self.handle, name.ptr, name.len)) {
            return error.CompileFailed;
        }
    }

    pub fn emitObject(self: *CraneliftBackend) ![]u8 {
        var output: ?[*]u8 = null;
        var output_len: usize = 0;

        if (!cranelift_emit_object(self.handle, &output, &output_len)) {
            return error.EmitFailed;
        }

        if (output) |ptr| {
            // 复制数据到 Zig 管理的内存
            const data = try self.allocator.alloc(u8, output_len);
            @memcpy(data, ptr[0..output_len]);

            // 释放 Rust 分配的内存
            cranelift_free_buffer(ptr, output_len);

            return data;
        }

        return error.EmitFailed;
    }
};

// 测试
test "cranelift backend init" {
    const allocator = std.testing.allocator;
    var backend = try CraneliftBackend.init(allocator);
    defer backend.deinit();
}

test "cranelift compile function" {
    const allocator = std.testing.allocator;
    var backend = try CraneliftBackend.init(allocator);
    defer backend.deinit();

    try backend.compileSimpleFunction("test_func");
}
